package com.hue.gulimail.gulimailproduct.web;

import com.hue.gulimail.gulimailproduct.entity.CategoryEntity;
import com.hue.gulimail.gulimailproduct.service.CategoryService;
import com.hue.gulimail.gulimailproduct.vo.Catalog2Vo;
import org.redisson.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Controller
public class IndexController {
    @Autowired
    private CategoryService categoryService;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 得到所有的1级分类
     *
     * @return
     */
    @GetMapping({"/", "/index.html"})
    public String getCategory1(Model model) {
        List<CategoryEntity> categoryList = categoryService.getCategory1();
        model.addAttribute("categorys", categoryList);
        return "index";
    }


    /**
     * 返回所有分类信息
     *
     * @return
     */
    @ResponseBody
    @GetMapping("index/json/catalog.json")
    public Map<String, List<Catalog2Vo>> catalogJson() {
        Map<String, List<Catalog2Vo>> data = categoryService.getAllCatalog();
        return data;
    }

    /**
     * 测试Redisson分布式锁：
     * 1.redisson解决了锁的自动续期问题，当业务时间超时时锁会自动续期为30s
     * 2.加锁的任务只要运行完成了，就不会给锁续期了，就算没有手动解锁，也会在30s后删除锁
     */
    @GetMapping("/hello")
    @ResponseBody
    public String hello() {
        //获取到分布式锁,只要名字一样就是同一把锁
        RLock lock = redissonClient.getLock("lock01");
        /**lock()方法的功能:
         * 阻塞式等待，没有抢到锁会阻塞，抢到锁之后锁的默认时间为30s
         * 1.redisson解决了锁的自动续期问题，当业务时间超时时锁会自动续期为30s(每隔1/3的看门狗时间就自动续期)
         * 2.加锁的任务只要运行完成了，就不会给锁续期了，就算没有手动解锁，也会在30s后删除锁
         */
//        lock.lock();

        /**
         * lock(time,TimeUnit)的功能:
         * 1.在获取的锁的时候设置了锁的过期时间，在过期后不会续期
         * 所以在使用这个方法的时候必须要设置过期时间大于业务的时间
         * 但是在真正业务中还是推荐使用手动设置过期时间，可以尽量把过期时间设置大一点
         */
        lock.lock(30, TimeUnit.SECONDS);
        try {
            System.out.println("线程" + Thread.currentThread().getId() + "获取到锁...");
            Thread.sleep(30000);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("释放锁...");
            lock.unlock();
        }
        return "hello";
    }

    /**
     * 测试读写锁:
     * 1.写锁是一个排它锁(互斥锁),当读锁没有释放的时候，写锁必须等待
     * 2.读锁是一个共享锁，当写锁没有释放的时候，读锁必须等待
     */
    @GetMapping("/write")
    @ResponseBody
    public String write() {
        //获取读写锁
        RReadWriteLock lock = redissonClient.getReadWriteLock("rw-lock");
        //获取写锁
        RLock writeLock = lock.writeLock();
        //加写锁
        writeLock.lock();
        String uuid = null;
        try {
            //向redis中写入数据
            uuid = UUID.randomUUID().toString();
            Thread.sleep(30000);
            redisTemplate.opsForValue().set("value", uuid);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            writeLock.unlock();
        }
        return uuid;
    }

    @GetMapping("/read")
    @ResponseBody
    public String read() {
        //获取读写锁
        RReadWriteLock lock = redissonClient.getReadWriteLock("rw-lock");
        //获取读锁
        RLock readLock = lock.readLock();
        readLock.lock();
        String uuid = null;
        try {
            //读取数据
            uuid = redisTemplate.opsForValue().get("value");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readLock.unlock();
        }
        return uuid;
    }

    /**
     * 分布式信号量测试
     * 信号量可以用作分布式的限流
     */
    @GetMapping("/park")
    @ResponseBody
    public String park() {
        RSemaphore semaphore = redissonClient.getSemaphore("park");
        try {
            //获取一个信号量,阻塞式的
            semaphore.acquire();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "park";
    }

    @GetMapping("/go")
    @ResponseBody
    public String go() {
        RSemaphore semaphore = redissonClient.getSemaphore("park");
        try {
            //释放一个信号量
            semaphore.release();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "go";
    }

    /**
     * Redisson的闭锁CountDownLauch
     */
    @GetMapping("/door")
    @ResponseBody
    public String door() {
        //获取闭锁
        RCountDownLatch door = redissonClient.getCountDownLatch("door");
        door.trySetCount(3);
        try {
            //当计数为0的时候停止阻塞
            door.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "锁门...";
    }

    @GetMapping("/gogo")
    @ResponseBody
    public String gogo() {
        RCountDownLatch door = redissonClient.getCountDownLatch("door");
        try {
            //计数-1
            door.countDown();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "放假...";
    }
}
